package com.hsu.hsurpc.loadbalancer;

import com.hsu.hsurpc.model.ServiceMetaInfo;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 一致性哈希负载均衡
 * @Author Hsu琛君珩
 * @Date 2024-09-24 15:43
 * @Description
 * @Version: v1.0.0
 */
public class ConsistentHashLoadBalancer implements LoadBalancer {

    /**
     * 一致性 Hash 环，存放虚拟节点
     * 使用 TreeMap 实现，虚拟节点的哈希值作为 key，实际服务节点作为 value。
     */
    private final TreeMap<Integer, ServiceMetaInfo> virtualNodes = new TreeMap<>();

    /**
     * 虚拟节点数
     * 每个服务节点对应的虚拟节点数量，用于平衡负载和提升一致性哈希的分布效果。
     */
    private static final int VIRTUAL_NODE_NUM = 100;

    /**
     * 从服务节点列表中选择一个节点
     * 构建一致性哈希环，将服务节点映射到哈希环上，根据请求的哈希值选择最接近的服务节点。
     *
     * @param requestParams      请求参数，用于生成请求的哈希值，从而在哈希环上选择节点
     * @param serviceMetaInfoList 服务节点的元数据信息列表，包含所有可用的服务节点
     * @return 返回一致性哈希算法选择的服务节点的元数据信息
     */
    @Override
    public ServiceMetaInfo select(Map<String, Object> requestParams, List<ServiceMetaInfo> serviceMetaInfoList) {
        if (serviceMetaInfoList.isEmpty()) {
            return null;
        }
        // 构建虚拟节点环
        for (ServiceMetaInfo serviceMetaInfo : serviceMetaInfoList) {
            for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
                int hash = getHash(serviceMetaInfo.getServiceAddress() + "#" + i);
                virtualNodes.put(hash, serviceMetaInfo);
            }
        }
        // 获取调用请求的 Hash 值
        int hash = getHash(requestParams);
        // 选择最接近且大于等于调用请求的 hash 值的虚拟节点
        Map.Entry<Integer, ServiceMetaInfo> entry = virtualNodes.ceilingEntry(hash);
        if (entry == null) {
            // 如果没有大于等于调用请求 hash 值的虚拟节点，则返回环首部的节点
            entry = virtualNodes.firstEntry();
        }
        return entry.getValue(); // 返回选择的服务节点
    }

    /**
     * 获取对象的哈希值
     * 使用 Java 内置的 hashCode 方法计算哈希值，通常可以结合具体需求进行哈希函数的优化。
     *
     * @param key 需要计算哈希值的对象
     * @return 返回计算的哈希值
     */
    private int getHash(Object key) {
        return key.hashCode();
    }
}
